#include "vl_semphore.h"


#ifdef VL_POSIX
	#include <sys/ipc.h>
	#include <sys/types.h>
	#include <sys/sem.h>
#endif

using namespace VL;


/**
 * @Author: CimZzz
 * @Date: 2022-08-17 11:02:20
 * 
 * 构造方法
 * 
 * @param key 值指针
 * @param notCreate 不存在信号量的时候是否进行创建
 */
Semphore::Semphore(std::shared_ptr<void> keyPtr, bool notCreate): key(keyPtr), notCreate(notCreate), semId(nullptr), numsOfSem(0), semArr(nullptr), isInited(false) {
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-17 11:02:20
 * 
 * 构造方法
 * 
 * @param key 值指针
 * @param numsOfSem 信号量数量
 * @param semArray 信号量初始值数组
 * @param notCreate 不存在信号量的时候是否进行创建
 */
Semphore::Semphore(std::shared_ptr<void> keyPtr, int numsOfSem, unsigned short * semArray, bool notCreate): key(keyPtr), notCreate(notCreate), semId(nullptr), numsOfSem(numsOfSem), semArr(semArray, [](unsigned short ptr[]){
	delete[] ptr;
}), isInited(false) {

}

/**
 * @Author: CimZzz
 * @Date: 2022-08-17 11:02:37
 * 
 * 析构函数1
 */
Semphore::~Semphore() {
	this->release();
}


/**
 * @Author: CimZzz
 * @Date: 2022-08-17 11:17:27
 * 
 * 初始化信号量
 * 
 * @return 初始化成功返回 true
 */
bool Semphore::initSemphore() {
	if(this->isInited) {
		return true;
	}
	#ifdef VL_POSIX
		auto keyPtr = std::static_pointer_cast<key_t, void>(this->key);
		int semId = -1;
		if(!this->notCreate) {
			semId = semget(*keyPtr, this->numsOfSem, 0666 | IPC_CREAT | IPC_EXCL);
		}
		if(semId == -1) {
			semId = semget(*keyPtr, this->numsOfSem, 0666);
		}

		if(semId == -1) {
			// 获取失败, 返回 false
			return false;
		}

		if(!this->notCreate) {
			if(this->numsOfSem > 1) {
				// 超过 1 个信号量
				if(semctl(semId, this->numsOfSem - 1, SETALL, this->semArr.get()) == -1) {
					return false;
				}
			} else {
				// 单一信号量
				if(semctl(semId, 1, IPC_SET) == -1) {
					return false;
				}
			}
		}
		
		this->semId = std::shared_ptr<int>(new int(semId));

	#endif //VL_POSIX

	this->isInited = true;
	
	return true;
}


/**
 * @Author: CimZzz
 * @Date: 2022-08-17 17:23:01
 * 
 * 标记需要创建信号量
 */
void Semphore::markNeedCreate() {
	if(this->isInited) {
		return;
	}
	this->notCreate = false;
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-17 15:18:56
 * 
 * 标记为不创建信号量
 */
void Semphore::markNotCreate() {
	if(this->isInited) {
		return;
	}
	this->notCreate = true;
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-17 11:32:45
 * 
 * 信号量 P 操作
 * 
 * @param index 信号量下标
 * 
 * @note 如果只有一个信号量, index 参数将会被忽略
 * 
 * @return 执行成功返回 true
 */
bool Semphore::p(uint16_t index) {
	if(!this->isInited) {
		return false;
	}

	#ifdef VL_POSIX
		auto semIdPtr = std::static_pointer_cast<int, void>(this->semId);
		if(this->numsOfSem > 1) {
			if(index >= this->numsOfSem) {
				return false;
			}
			struct sembuf sem_b;
			sem_b.sem_num = index;
			sem_b.sem_op = -1;//P()
			sem_b.sem_flg = SEM_UNDO;
			if(semop(*semIdPtr, &sem_b, 1) == -1) {
				// 执行 P 操作失败
				return false;
			}
		} else {
			struct sembuf sem_b;
			sem_b.sem_num = 0;
			sem_b.sem_op = -1;//P()
			sem_b.sem_flg = SEM_UNDO;
			if(semop(*semIdPtr, &sem_b, 1) == -1) {
				// 执行 P 操作失败
				return false;
			}
		}
	#endif //VL_POSIX
	
	return true;
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-17 11:32:59
 * 
 * 信号量 V 操作
 * 
 * @param index 信号量下标
 * 
 * @note 如果只有一个信号量, index 参数将会被忽略
 * 
 * @return 执行成功返回 true
 */
bool Semphore::v(uint16_t index) {
	if(!this->isInited) {
		return false;
	}

	#ifdef VL_POSIX
		auto semIdPtr = std::static_pointer_cast<int, void>(this->semId);
		if(this->numsOfSem > 1) {
			if(index >= this->numsOfSem) {
				return false;
			}
			struct sembuf sem_b;
			sem_b.sem_num = index;
			sem_b.sem_op = 1;//V()
			sem_b.sem_flg = SEM_UNDO;
			if(semop(*semIdPtr, &sem_b, 1) == -1) {
				// 执行 V 操作失败
				return false;
			}
		} else {
			struct sembuf sem_b;
			sem_b.sem_num = 0;
			sem_b.sem_op = 1;//V()
			sem_b.sem_flg = SEM_UNDO;
			if(semop(*semIdPtr, &sem_b, 1) == -1) {
				// 执行 V 操作失败
				return false;
			}
		}
	#endif //VL_POSIX
	
	return true;
}


/**
 * @Author: CimZzz
 * @Date: 2022-08-17 16:07:38
 * 
 * 释放信号量
 * 
 * @return 释放成功返回 true
 */
bool Semphore::release() {
	if(!this->isInited) {
		return true;
	}
	if(this->isInited && !this->notCreate) {
		// 释放信号量
		#ifdef VL_POSIX
			auto idPtr = std::static_pointer_cast<int, void>(this->semId);
			union semun sem_union;
			if(semctl(*idPtr, 0, IPC_RMID, sem_union) == -1) {
				return false;
			}
		#endif //VL_POSIX
	}
	this->isInited = false;
	this->semId.reset();
	return true;
}

#ifdef VL_POSIX
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-17 11:48:55
	 * 
	 * 生成信号量 Key 值
	 * 
	 * @param path 项目路径
	 * @param id 项目编号
	 * 
	 * @return 信号量 Key 值
	 */
	std::shared_ptr<void> Semphore::generateKey(const char * path, int id) {
		std::shared_ptr<void> keyPtr;

		#ifdef VL_POSIX
			key_t key = ftok(path, id);
			if(key == -1) {
				return keyPtr;
			}

			keyPtr = std::shared_ptr<key_t>(new key_t(key));
		#endif //VL_POSIX

		return keyPtr;
	}
#endif //VL_POSIX